﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Drawing;
using System.IO;
using Svg;
using Svg.Pathing;
using System.Drawing.Imaging;
using System.Xml;

namespace CsPotrace
{
    public enum TraceType
    {
        TRACE_BRIGHTNESS,
        TRACE_BRIGHTNESS_MULTI,
        TRACE_CANNY,
        TRACE_QUANT,
        TRACE_QUANT_COLOR,
        TRACE_QUANT_MONO
    }
    public class CsPotraceEngine
    {
        private TraceType _traceType;
        bool _MultiScanSmooth;
        int _MultiScanNrColors;
        ArrayList ListOfCurveArray;
        public SvgGroup trace(Bitmap bm)
        {
            switch (_traceType)
            {
                case TraceType.TRACE_BRIGHTNESS:
                    return null;
                case TraceType.TRACE_BRIGHTNESS_MULTI:
                    return null;
                case TraceType.TRACE_CANNY:
                    return null;
                case TraceType.TRACE_QUANT:
                    return null;
                case TraceType.TRACE_QUANT_COLOR:
                case TraceType.TRACE_QUANT_MONO:
                    return traceQuant(bm);
                default:
                    return null;
            }
        }
        private SvgGroup traceQuant(Bitmap bm)
        {

            IndexedMap iMap = filterIndexed(bm, this._MultiScanSmooth, this._MultiScanNrColors, this._traceType);
            GrayMap gm = new GrayMap(iMap.width, iMap.height);
            for (int row = 0; row < gm.height; row++)
            {
                for (int col = 0; col < gm.width; col++)
                {
                    gm.SetPixel(col, row, (int)GrayMapEnum.WHITE);
                }   
            }
            List<SvgElement> svgElementCollection = new List<SvgElement>();
            for (int colorIndex = 0; colorIndex < iMap.nrColors; colorIndex++)
            {
                 /*Make a gray map for each color index */
                for (int row = 0; row < iMap.height; row++)
                {
                    for (int col = 0; col < iMap.width; col++)
                    {
                        int indx = iMap.GetPixel(col, row);
                        if (indx == colorIndex)
                            gm.SetPixel(col, row, (int)GrayMapEnum.BLACK);
                        else
                            gm.SetPixel(col, row, (int)GrayMapEnum.WHITE);
                    }
                }
                long nodeCount = 0;

                SvgGroup d = grayMapToPath(gm, ref nodeCount);
                Color rgb = iMap.clut[colorIndex];
                foreach (SvgPath item in d.Children)
                {
                    item.Fill = new SvgColourServer(rgb);
                }
                svgElementCollection.Add(d);
            }
            SvgGroup group = new SvgGroup();
            for (int j = svgElementCollection.Count-1; j >= 0; j--)
            {
                group.Children.Add(svgElementCollection[j]);
            }
            return group;
        }
        private long writePaths(ArrayList plist, ref SvgGroup data, ref List<PointF> points)
        {
            long nodeCount = 0L;
            if (ListOfCurveArray == null) return 0;
            for (int i = 0; i < ListOfCurveArray.Count; i++)//node
            {
                ArrayList CurveArray = (ArrayList)ListOfCurveArray[i];
                SvgGroup ContourG = null;
                SvgGroup HoleG = new SvgGroup();
                SvgPath Contour = null;
                SvgPath Hole = null;
                SvgPath Current = null;
                nodeCount++;
                
                for (int j = 0; j < 1; j++)
                {
                    if (j == 0)
                    {
                        float x2 = (float)((Potrace.Curve)((Potrace.Curve[])CurveArray[j])[0]).B.x;
                        float y2 = (float)((Potrace.Curve)((Potrace.Curve[])CurveArray[j])[0]).B.y;
                        Contour = new SvgPath();
                        ContourG = new SvgGroup();
                        Current = Contour;
                        Current.PathData.Add(new SvgMoveToSegment(new PointF(x2,y2)));
                    }
                    else
                    {
                        HoleG = new SvgGroup();
                        Hole = new SvgPath();
                        Current = Hole;
                    }
                    Potrace.Curve[] Curves = (Potrace.Curve[])CurveArray[j];
                    float factor = 1;//Zoom
                    for (int k = 0; k < Curves.Length; k++)
                    {
                        if (Curves[k].Kind == Potrace.CurveKind.Bezier)
                        {
                            PointF start = new PointF((float)Curves[k].A.x * factor, (float)Curves[k].A.y * factor);
                            PointF firstControlPoint = new PointF((float)Curves[k].ControlPointA.x * factor, (float)Curves[k].ControlPointA.y * factor);
                            PointF secondControlPoint = new PointF((float)Curves[k].ControlPointB.x * factor, (float)Curves[k].ControlPointB.y * factor);
                            PointF end = new PointF((float)Curves[k].B.x * factor, (float)Curves[k].B.y * factor);
                            SvgCubicCurveSegment curve = new SvgCubicCurveSegment(start, firstControlPoint, secondControlPoint, end, false);
                            Current.PathData.Add(curve);
                        }
                        else
                        {
                            PointF start = new PointF((float)Curves[k].A.x * factor, (float)Curves[k].A.y * factor);
                            PointF end = new PointF((float)Curves[k].B.x * factor, (float)Curves[k].B.y * factor);
                            SvgLineSegment line = new SvgLineSegment(start,end,false);
                            Current.PathData.Add(line);
                        }
                    }
                    if (j == 0)
                    {
                        Current.PathData.Add(new SvgClosePathSegment());
                        ContourG.Children.Add(Current);
                    }
                    //if (j > 0)
                    //{
                    //    Hole.PathData.Add(new SvgClosePathSegment());
                    //    HoleG.Children.Add(Hole);
                    //}
                }
                //ContourG.Children.Add(HoleG);
                data.Children.Add(Current);
            }
            return nodeCount;

        }
        private SvgGroup grayMapToPath(GrayMap grayMap, ref long nodeCount)
        {
            Bitmap bm = new Bitmap(grayMap.width,grayMap.height);
            for (int y = 0; y < grayMap.height; y++)
            {
                for (int x = 0; x < grayMap.width; x++)
                {
                    bm.SetPixel(x, y, Color.FromArgb(grayMap.GetPixel(x, y)));
                }
            }

            bool[,] Matrix = Potrace.BitMapToBinary(bm, 130);
            Potrace.potrace_trace(Matrix, ListOfCurveArray);
            SvgGroup data = new SvgGroup();
            List<PointF> points = new List<PointF>();
            nodeCount = writePaths(ListOfCurveArray, ref data, ref points);

            return data;
        }
        public CsPotraceEngine()
        {
            Potrace.turdsize = 2;//Ignore Area
            Potrace.alphamax = 1.00;//Corner threshold
            Potrace.opttolerance = 0.2;//Tolerance
            //optimize the path p, replacing sequences of Bezier segments by a
            //single segment when possible.
            Potrace.curveoptimizing = true;//optimize
            this._MultiScanSmooth = true;
            this._MultiScanNrColors = 8;
            this._traceType = TraceType.TRACE_QUANT_COLOR;
            ListOfCurveArray = new ArrayList();
        }
        private IndexedMap filterIndexed(Bitmap bm, bool isMultiScanSmooth, int multiScanNrColors, TraceType traceType)
        {
            if (bm == null)
                return null;

            IndexedMap newGm = null;

           // RgbMap gm = gdkPixbufToRgbMap(bm);
            if (isMultiScanSmooth)
            {
                //RgbMap gaussMap = rgbMapGaussian(bm);
                Bitmap gaussMap = Filterset.rgbMapGaussian(bm);
                newGm = Quantize.rgbMapQuantize(gaussMap, multiScanNrColors);
            }
            else
            {
                newGm = Quantize.rgbMapQuantize(bm, multiScanNrColors);
            }

            if (traceType == TraceType.TRACE_QUANT_MONO)
            {
                //Turn to grays
                //for (int i = 0; i < newGm->nrColors; i++)
                //{
                //    RGB rgb = newGm->clut[i];
                //    int grayVal = (rgb.r + rgb.g + rgb.b) / 3;
                //    rgb.r = rgb.g = rgb.b = grayVal;
                //    newGm->clut[i] = rgb;
                //}
            }

            return newGm;
        }
    }
}
